#!/usr/bin/python

#Library used by all the scripts
import os, serial
from time import time,sleep
import struct

def getOptions():
	#Read the file into which there are all the options:
	#kernelPath, delay, etc...
	pwd = os.getcwd()
	f = open(pwd+'/../usersettings','r')
	tmpArray = f.readlines()
	f.close()
	options ={} 
	#The config is like option=value so lets create a hash table
	for string in tmpArray :
		explodedString = string.rsplit("=")
		options[explodedString[0]] = explodedString[1][0:-1]

	return options

######## Generate Kernel Code Functions ############
def generateKernelCode(bootMode, automaticReboot, deltaTable, limit,assessInitCalls='NO'):
	#Format deltaTable so that all the arrays have the same number of
	#of cells
	deltaTable = formatDeltaTable(deltaTable)

	#Generates only the header file
	headerFileContent =  '#define PARALLEL 1\n#define REGULAR 0\n#define YES 1\n#define NO 0\n'
	headerFileContent += 'static int __init isOrphan(int);\n'
	headerFileContent += 'static void __init initThread(int index);\n'
	headerFileContent += 'static void __init launchScript(int index);\n'
	headerFileContent += 'atomic_t goOn = ATOMIC_INIT(0);\n'
	headerFileContent += 'long load;\n'
	headerFileContent += 'int mode;\n' 
	headerFileContent += '/*Here put REGULAR or PARALLEL depending on what you want*/\n' 
	headerFileContent += 'int boot_mode ='+ bootMode +';\n'
	headerFileContent += 'int automatic_reboot =' + automaticReboot +';\n'
	headerFileContent += 'int do_deffered_initcalls = NO;\n' #Should be modified later...
	headerFileContent += 'int assess_initcalls ='+ assessInitCalls+';\n'
	headerFileContent += '#define NBR_OF_INITCALLS '+ str(len(deltaTable)) +'\n'
	headerFileContent += 'pid_t pidList[NBR_OF_INITCALLS];\n'
	headerFileContent += 'int positionInPidList = 0;\n'
	headerFileContent += '#define GRAPHDEPTH '+str(getGraphDepth(deltaTable))+'\n' 
	headerFileContent += '#define LIMIT '+str(limit)+'\n'
	#Now we can generate deltaList
	headerFileContent += 'long deltaList[NBR_OF_INITCALLS][2] = {\n'
	for node  in deltaTable:
		headerFileContent += '{'
		headerFileContent += str(node.name)+','
		headerFileContent += str(node.delta) +'},\n'
	headerFileContent = headerFileContent[0:-2]
	headerFileContent += '};\n'

	#Generate the dependenciesList
	headerFileContent += 'int dependenciesList[NBR_OF_INITCALLS][GRAPHDEPTH] = { \n'
	for i in range(0,len(deltaTable)):
		index = indexWhereNodeIs(i,deltaTable)
		headerFileContent += '{'
		for parent in deltaTable[index].parents:
			headerFileContent += str(parent)+','
		headerFileContent = headerFileContent[0:-1]
		headerFileContent += '},\n'
	headerFileContent = headerFileContent[0:-2]
	headerFileContent += '};\n'

	#The header file can now be written in the kernel
	options =  getOptions()
	f = open(options['kernelPath']+'/init/bootTables.h','w')
	f.write(headerFileContent)
	f.close()

def getGraphDepth(deltaTable):
	depth = 0
	for node in deltaTable:
		if len(node.parents) > depth:
			depth = len(node.parents)
	
	return depth

def formatDeltaTable(deltaTable):
	graphDepth = getGraphDepth(deltaTable)
	
	for node in deltaTable:
		parentArrayLen = len(node.parents)
		if parentArrayLen != graphDepth:
			for i in range(parentArrayLen,graphDepth):
				node.parents.append(-1)
	return deltaTable

def indexWhereNodeIs(i,deltaTable):
	for index, node in enumerate(deltaTable):
		if node.name == i:
			return index

######### End of Generate Kernel Code Functions #########

######## Get the input from the board and analyze it #######

#We use the pyserial lib. : http://pyserial.sourceforge.net
class serialPortInterface:
	def __init__(self, portNumber):
		#Then get the name of the device corresponding to the number...
		self.port = portNumber
		f = open("./../config",'r')
		lines = f.readlines()
		f.close()

		line = lines[portNumber]
		self.devname = line.split("=")[1][:-1]		

	def openConnection(self):
		self.ser = serial.Serial(self.devname,115200, timeout=1)
	def closeConnection(self):
		self.ser.close()
	def getInputStream(self):
		#Read one line and returns it
		return self.ser.readline()
	
	def analyzeInputStream(self,action):
		#Open a connection, then waits for the board to
		#send either an error or a success message
		#If it can't read anything within the delay set by the user
		#then it assumes there was an error
		self.openConnection()
		self.ser.flushInput()
		self.ser.flushOutput()
		while 1 < 2:
			if action == 'getNbrOfInitCalls':
				line = self.getInputStream()
				print line[0:-1]
				if line.find("!*!$$!*NbrOfInitCalls")!= -1 :
					indexInLine = line.find("!*!$$!*NbrOfInitCalls")
					nbrOfInitCalls = int(line[21 + indexInLine:-1])	
					self.closeConnection()
					return nbrOfInitCalls
			if action == 'getBootTime':
				line = self.getInputStream()
				print line[0:-1]
				if line.find("!*!$$!*BootTime")!= -1:
					indexInLine = line.find("!*!$$!*BootTime")
					bootTime = int(line[15 + indexInLine:-1])
					self.closeConnection()	
					return bootTime
			if action == 'errorDetection':
				line = self.getInputStream()
				print line[0:-1]
				if line.find("!*!$$!*!Error!*!$$!*!") != -1:
					self.closeConnection()
					return -1
					
				if line.find("!*!$$!*!OK!*!$$!*!") != -1:
					self.closeConnection()
					return 0
				if line.find("YAMON ROM Monitor") != -1:
					self.closeConnection()
					return -1 
			if action == 'getDelta':
				line = self.getInputStream()
				print line[0:-1]
				if line.find("!*!$$!*!Delta")!= -1 :
					indexInLine = line.find("!*!$$!*!Delta")
					tps = int(line[13 + indexInLine:-1])	
					self.closeConnection()
					return tps
			if action == 'getBoardIP':
				line = self.getInputStream()
				print line[0:-1]
				if line.find("ipaddr:     ")!= -1 :
					indexInLine = line.find("!*!$$!*!Delta")
					ip = line[14 + indexInLine:-1]	
					self.closeConnection()
					return ip

		return -1		

	def writeCommand(self,command):
		self.openConnection()
		self.ser.flushInput()
		#Now we don't know if the kernel is faster to compile
		#than the board to reboot so let's check...
		line = ""
		line = self.ser.readline()
		if line != "":
			input = ""
			i = 0
			while input.find("YAMON ROM Monitor")== -1 and i<30:
				i += 1
				input = self.ser.readline()	
				sleep(1)
		if self.ser.isOpen() == 'False':
			self.openConnection()
		sleep(5)
		self.ser.write(command)	
		self.closeConnection()


########## End of the serial port related functions ###############

########## Definition of the node object ####################

class Node:
	def __init__(self,name):
		self.name = name
		self.delta = 1
		self.parents = []
	
def getHostIP():
	import socket
	import fcntl
   	ifname='eth0'
	s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    	return socket.inet_ntoa(fcntl.ioctl(
        	s.fileno(),
        	0x8915,  # SIOCGIFADDR
        	struct.pack('256s', ifname[:15])
    	)[20:24])

def compileKernel():
	linuxKernelPath = getOptions()['kernelPath']
	ret=os.system('make all -C '+linuxKernelPath + ' && make -C '+linuxKernelPath+' modules  && make -C '+linuxKernelPath+' modules_install && make -C ' +linuxKernelPath + ' vmlinux.bin' )		
	if ret != 0 :
		print "Compilation Error"
		exit()
	
def createSymbolicLinkToKernelImage():
	linuxKernelPath = getOptions()['kernelPath']
	tftpRootPath = getOptions()['tftpRootPath']
	os.system('ln -s '+linuxKernelPath+'/arch/mips/boot/vmlinux.bin '+tftpRootPath)
